#pragma once

#include <AVUIHash.h>
#include <string.h>

namespace AVUI {

class BoxedValueType : public Object
{
public:
    DECLARE_ELEMENT(BoxedValueType, Object);

    BoxedValueType() { };

    virtual const Type& get_ContainedType() const = 0;
    virtual void* GetContainedValueOffset() = 0;
};

class BoxedValueTypeInstanceBase : public BoxedValueType
{
public:

    BoxedValueTypeInstanceBase() { };

    virtual const Type& get_Type() const { return get_ContainedType(); };
    virtual bool IsType(const Type& rhs) const
    { 
        if(get_ContainedType() == rhs)
        {
            return true;
        }
        return BoxedValueType::IsType(rhs);
    } 
};

template<class T>
class BoxedValueTypeInstance : public BoxedValueTypeInstanceBase
{
public:
    BoxedValueTypeInstance(const T& t) : m_value(t) { };

    virtual bool Equals(const Object* pObject) const
    {
        if(pObject == NULL)
        {
            return false;
        }

        if(pObject->get_Type() != get_Type())
        {
            return false;
        }

        return m_value == ((const BoxedValueTypeInstance<T> *) pObject)->m_value;
    }

    virtual const Type& get_ContainedType() const { return AVUI::get_Type((T*) 0); }
    T get_Value() const { return m_value; }

    virtual void* GetContainedValueOffset() { return &m_value; }

    virtual int GetHashCode() { return Hash::HashData((const unsigned char*) &m_value, sizeof(m_value), 0); }

private:
    T m_value;
};

template<>
class BoxedValueTypeInstance<const char*> : public BoxedValueTypeInstanceBase
{
public:
    BoxedValueTypeInstance(const char* szValue) 
    { 
        if(szValue == NULL)
        {
            m_szValue = NULL;
            return;
        }

        int cch = (int) strlen(szValue);

        m_szValue = (char*)(Object::AllocateMemory((cch + 1) * sizeof(char)));
        memcpy(m_szValue, szValue, cch);
        m_szValue[cch] = '\0';
    };

    virtual bool Equals(const Object* pObject) const
    {
        if(pObject == NULL)
        {
            return false;
        }

        if(pObject->get_Type() != get_Type())
        {
            return false;
        }

        return !strcmp(m_szValue, ((const BoxedValueTypeInstance<const char*> *) pObject)->m_szValue);
    }

    virtual const Type& get_ContainedType() const { return GET_TYPE(const char*); }
    const char* get_Value() const { return m_szValue; }

    virtual void* GetContainedValueOffset() { return &m_szValue; }

    virtual int GetHashCode() { if(m_szValue == NULL) return 0; return Hash::HashData((unsigned char*)m_szValue, (int) (strlen(m_szValue) + 1), 0); }

private:
    char* m_szValue;
};

template<class T>
extern ObjectPtr BoxValue(const T& t)
{
    return ObjectPtr(new BoxedValueTypeInstance<T>(t), false);
}

template<class T>
T UnboxValue(const Object* pObject)
{
    // minimum possible check - this will be a hotspot.
    if(!pObject->IsType(AVUI::get_Type((T*) 0)))
    {
        LibraryCriticalError();
    }

    return ((BoxedValueTypeInstance<T> *) pObject)->get_Value();
}

class BooleanBoxes
{
public:
    static Object* get_TrueBox() 
    {
        static BoxedValueTypeInstance<bool> trueBox(true);
        return &trueBox;
    }

    static Object* get_FalseBox() 
    {
        static BoxedValueTypeInstance<bool> falseBox(false);
        return &falseBox;
    }

    static Object* Box(bool value)
    {
        if(value) 
        {
            return get_TrueBox();
        }
        return get_FalseBox();
    }
};


}; // namespace AVUI

